package com.suduku.calc;

import com.suduku.entity.Box;
import com.suduku.util.SudoUtil;

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * XYZ-wing（如果一个宫内有B1[a,b,c]，
 * 如果所在行存在B2[a,c]，且同宫内不在B1行中存在B3[b,c]，
 * 则在当前宫内，B1同行的空白格候选值不会存在数字c。
 *
 * 同理如果所在列存在B2[a,c]，且同宫内不在B1列中存在B3[b,c]，
 * 则在当前宫内，B1同列的空白格候选值不会存在数字c） <br/>
 *
 * 有待优化
 *
 * 测试数据：DataConstant.OTHER_XYZ_WING_01
 */
public class XYZwingCalc extends AbstractCalc {

    @Override
    Box solve() {
        // 遍历所有宫
        Set<Map.Entry<Integer, List<Box>>> entries = getGMap().entrySet();
        for (Map.Entry<Integer, List<Box>> entry : entries) {
            List<Box> list = entry.getValue();
            // 遍历当前宫
            for (Box b1 : list) {
                // 确定B1单元格
                if(b1.isBlank() && b1.getCList().size() == 3) {
                    // 获取所在行，确定B2单元格（使用JDK8的函数编程，可以进一步抽象）
                    Box clearBox = findB2AndClear(list, getXList(b1), b1, Box::getX);
                    if(clearBox != null) {
                        return clearBox;
                    }
                    // 获取所在列，确定B2单元格
                    clearBox = findB2AndClear(list, getYList(b1), b1, Box::getY);
                    if(clearBox != null) {
                        return clearBox;
                    }
                }
            }
        }
        return null;
    }

    /**
     * 功能描述: 查找B2单元格，并且清理数据 <br/>
     * 
     * @param gList 宫列表
     * @param areaList 区域列表，行区域，或列区域
     * @param b1 B1单元格
     * @param getFun 方法，可以是行，或列
     * @return "com.suduku.entity.Box"
     */
    private Box findB2AndClear(List<Box> gList, List<Box> areaList, Box b1, Function<Box, Integer> getFun) {
        for (Box b2 : areaList) {
            // 确定是B2单元格
            if(isB2Box(b1, b2)) {
                // 确定数字b，和数字c
                Integer b = SudoUtil.getOther(b1.getCList(), b2.getCList());
                // 尝试使用B2的两个数字分别当数字c，确定B3数字
                for (Integer c : b2.getCList()) {
                    Box clearBox = findB3BoxAndClear(gList, b, c, b1, b2, getFun);
                    if(clearBox != null) {
                        return clearBox;
                    }
                }
            }
        }
        return null;
    }

    /**
     * 功能描述: B2是空白格，并且与B1不在同一宫中，而且候选值只有两个，却都在B1的候选值中 <br/>
     *
     * @param b1 b1
     * @param b2 b2
     * @return "boolean"
     */
    private boolean isB2Box(Box b1, Box b2) {
        return b2.isBlank() && b2.getG() != b1.getG() && b2.getCList().size() == 2 && b1.getCList().containsAll(b2.getCList());
    }

    /**
     * 功能描述: B3是空白格，并且候选数字只有两个（保证与B1不一致），不在同一行或同一列，包含数字b和c <br/>
     * 
     * @param list 列表
     * @param b 数字b
     * @param c 数字c
     * @param b1 B1单元格
     * @param b2 B2单元格
     * @param getFun 方法，可以是行，或列
     * @return "com.suduku.entity.Box"
     */
    private Box findB3BoxAndClear(List<Box> list, Integer b, Integer c, Box b1, Box b2, Function<Box, Integer> getFun) {
        Box b3 = list.stream()
                .filter(box -> box.isBlank() && box.getCList().size() == 2
                        && !getFun.apply(box).equals(getFun.apply(b1))
                        && box.getCList().containsAll(Arrays.asList(b, c)))
                .findAny().orElse(null);
        if(b3 != null) {
            List<Box> clearList = list.stream()
                    .filter(box -> box.isBlank() && box.getI() != b1.getI()
                            && getFun.apply(box).equals(getFun.apply(b1))
                            && box.getCList().contains(c))
                    .collect(Collectors.toList());
            for (Box box : clearList) {
                box.removeCList(c);
                change(box, SudoUtil.getList(b1, b2, b3), c);
                return box;
            }
        }
        return null;
    }

}
